package com.basic.business.utils;

import java.security.SecureRandom;


public final class IdGenerator {

    private long twepoch = 1288834974657L; // Thu, 04 Nov 2010 01:42:54 GMT
    // 区域标志位数

    private final static long REGION_ID_BITS = 3L;

    // 机器标识位数
    private final static long WORKER_ID_BITS = 10L;

    // 序列号识位数
    private final static long SEQUENCE_BITS = 5L;

    // 区域标志ID最大值
    private final static long MAX_REGIONID = -1L ^ (-1L << REGION_ID_BITS);

    // 机器ID最大值
    private final static long MAX_WORKERID = -1L ^ (-1L << WORKER_ID_BITS);

    // 序列号ID最大值
    private final static long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);

    // 机器ID偏左移10位
    private final static long WORKERID_SHIFT = SEQUENCE_BITS;

    // 业务ID偏左移20位
    private final static long REGIONID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;

    // 时间毫秒左移23位
    private final static long TIMESTAMP_LEFTSHIFT = SEQUENCE_BITS + WORKER_ID_BITS + REGION_ID_BITS;

    private static long lastTimestamp = -1L;

    private long sequence;

    private long workerId;

    private final long regionId = 0;
    private static volatile IdGenerator idGenerator = new IdGenerator();

    private IdGenerator() {
//		System.out.println("IdGenerator.IdGenerator()" + AppConfigerLoader.getConfig("api.workerId", ""));
//		this.workerId = Long.parseLong(AppConfigerLoader.getConfig("api.workerId", ""));
//		this.regionId = Long.parseLong(AppConfigerLoader.getConfig("api.regionId", ""));
        // 如果超出范围就抛出异常
        if (this.workerId > MAX_WORKERID || this.workerId < 0) {
            throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
        }
        if (this.regionId > MAX_REGIONID || this.regionId < 0) {
            throw new IllegalArgumentException("regionId Id can't be greater than %d or less than 0");
        }
    }

    public static IdGenerator getInstance() {

        return idGenerator;

    }

    public IdGenerator(long workerId) {
        // 如果超出范围就抛出异常
        if (workerId > MAX_WORKERID || workerId < 0) {
            throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
        }
        this.workerId = workerId;
        //this.regionId = 0;
    }

    // public static long getId() {
    // return idGenerator.generate();
    // }

    public long generate() {
        return this.nextId(false, 0);
    }

    public String generateString() {
        return this.nextId(false, 0) + "";
    }

    public long generate(long busid) {
        return this.nextId(true, busid);
    }

    /**
     * 实际产生代码的
     *
     * @param isPadding
     * @param busId
     *
     * @return
     */
    private synchronized long nextId(boolean isPadding, long busId) {

        long timestamp = timeGen();
        long paddingnum = regionId;

        if (isPadding) {
            paddingnum = busId;
        }

        if (timestamp < lastTimestamp) {
            try {
                throw new Exception("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 如果上次生成时间和当前时间相同,在同一毫秒内
        if (lastTimestamp == timestamp) {
            // sequence自增，因为sequence只有10bit，所以和sequenceMask相与一下，去掉高位
            sequence = (sequence + 1) & SEQUENCE_MASK;
            // 判断是否溢出,也就是每毫秒内超过1024，当为1024时，与sequenceMask相与，sequence就等于0
            if (sequence == 0) {
                // 自旋等待到下一毫秒
                timestamp = tailNextMillis(lastTimestamp);
            }
        } else {
            // 如果和上次生成时间不同,重置sequence，就是下一毫秒开始，sequence计数重新从0开始累加,
            // 为了保证尾数随机性更大一些,最后一位设置一个随机数
            sequence = new SecureRandom().nextInt(10);
        }

        lastTimestamp = timestamp;

        return ((timestamp - twepoch) << TIMESTAMP_LEFTSHIFT) | (paddingnum << REGIONID_SHIFT) | (workerId << WORKERID_SHIFT) | sequence;
    }

    // 防止产生的时间比之前的时间还要小（由于NTP回拨等问题）,保持增量的趋势.
    private long tailNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    // 获取当前的时间戳
    protected long timeGen() {
        return System.currentTimeMillis();
    }


//    public static void main(String args[]) {
//
//        IdGenerator idGenerator = new IdGenerator(1);
//
//        for (int i = 0; i < 10; i++) {
//            System.out.println(idGenerator.generate());
//        }
//
//    }
}
